﻿// Inner Fire 游戏引擎库
// DxView DirectX视图管理类
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 管理相机等控制视图的项目。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-04-23

#include "dx_view.h"
#include "dx_graphics.h"
#include <game/core/timer.h>

namespace ifire::dx {

DxView::DxView(DxGraphics* dx) : dx_(dx) {}

void DxView::InitConfig() {
  auto& cfg = dx_->Config()["Camera"];
  auto is_camera = cfg("Type").equals("NORMAL");
  SetCameraType(is_camera ? CameraType::NORMAL : CameraType::SPHERICAL);
  camera_speed_ = cfg("Speed", 10.0f);
  frustum_culling_enabled_ = cfg("FrustumCulling", true);
  is_take_control_ = cfg("TakeControl", true);

  spherical_camera_.zoom_rate_ = cfg("ZoomSpeed", spherical_camera_.zoom_rate_);
  spherical_camera_.offset_rate_ = cfg("OffsetSpeed", spherical_camera_.offset_rate_);
  spherical_camera_.min_radius = cfg("MinRadius", spherical_camera_.min_radius);
  spherical_camera_.max_radius = cfg("MaxRadius", spherical_camera_.max_radius);
}

void DxView::SetClientSize(UINT width, UINT height) {
  client_width_ = width;
  client_height_ = height;
  aspect_ratio_ = static_cast<float>(client_width_) / client_height_;
}

float DxView::GetAspectRatio() const { return aspect_ratio_; }

void DxView::ResetCamera() {
  if (camera_type_ == CameraType::NORMAL) {
    // 设置默认相机位置
    camera_.SetPosition(XMFLOAT3(0.0f, 20.0f, -50.0f));
    // 设置FOV
    camera_.SetLens(XMConvertToRadians(60.0f), GetAspectRatio(), 0.1f, 1000.0f);
    // 设置投影矩阵
    projection_ = camera_.projection_;
  } else if (camera_type_ == CameraType::SPHERICAL) {
    // 设置投影矩阵
    XMMATRIX projection = XMMatrixPerspectiveFovLH(
        XMConvertToRadians(60.0f), GetAspectRatio(), 0.1f, 1000.0f);
    XMStoreFloat4x4(&projection_, projection);
  }

  // 更新视椎体
  BoundingFrustum::CreateFromMatrix(
      camera_frustum_, XMLoadFloat4x4(&projection_));
}

void DxView::UpdateViewport() {
  screen_viewport_.TopLeftX = 0;
  screen_viewport_.TopLeftY = 0;
  screen_viewport_.Width = static_cast<float>(GetWidth());
  screen_viewport_.Height = static_cast<float>(GetHeight());
  screen_viewport_.MinDepth = 0.0f;
  screen_viewport_.MaxDepth = 1.0f;

  scissor_rect_ = {0, 0, GetWidth(), GetHeight()};
}

UIWidget DxView::GetClientWidget() const {
  UIWidget widget{};
  widget.size = GetClientSize();
  // 将位置置于中心
  widget.position = XMFLOAT2(widget.size.x * 0.5f, widget.size.y * 0.5f);
  widget.align = UIALIGN_MID_CENTER;
  widget.scale = {1.0f, 1.0f};
  widget.zindex = 0;
  return widget;
}

XMFLOAT2 DxView::GetClientSize() const {
  return XMFLOAT2(
      static_cast<float>(client_width_), static_cast<float>(client_height_));
}

void DxView::SetCameraType(CameraType type) { camera_type_ = type; }

void DxView::OnCameraInput() {
  if (!is_take_control_) {
    return;
  }

  const float dt = game::g_timer.fixedDeltaTime;
  if (camera_type_ == CameraType::NORMAL) {
    if (GetAsyncKeyState('W') & 0x8000)
      camera_.Walk(camera_speed_ * dt);

    if (GetAsyncKeyState('S') & 0x8000)
      camera_.Walk(-camera_speed_ * dt);

    if (GetAsyncKeyState('A') & 0x8000)
      camera_.Strafe(-camera_speed_ * dt);

    if (GetAsyncKeyState('D') & 0x8000)
      camera_.Strafe(camera_speed_ * dt);
  }
}

void DxView::UpdateCamera() {
  if (camera_type_ == CameraType::SPHERICAL) {
    // 设置VIEW
    spherical_camera_.UpdateViewMatrix();
    XMStoreFloat4x4(&view_, spherical_camera_.GetViewMatrix());
    eye_pos_ = spherical_camera_.GetPosition();
  } else if (camera_type_ == CameraType::NORMAL) {
    camera_.UpdateViewMatrix();
    view_ = camera_.view_;
    projection_ = camera_.projection_;
    eye_pos_ = camera_.GetPosition();
  }
}

void DxView::OnMouseMove(WPARAM btn_state, float move_x, float move_y) {
  if (!is_take_control_) {
    return;
  }

  if (camera_type_ == CameraType::SPHERICAL) {

    if ((btn_state & MK_LBUTTON) != 0) {
      spherical_camera_.OnRotate(move_x, move_y);
    } else if ((btn_state & MK_RBUTTON) != 0) {
      spherical_camera_.OnOffset(move_x, move_y);
    } else if ((btn_state & MK_MBUTTON) != 0) {
      // spherical_camera_.OnOffset(move_x, move_y);
    }

  } else if (camera_type_ == CameraType::NORMAL) {
    if ((btn_state & MK_LBUTTON) != 0) {
      // 使每个像素对应四分之一度。
      float dx = 0.25f * move_x;
      float dy = 0.25f * move_y;

      camera_.Pitch(XMConvertToRadians(dy));
      camera_.RotateY(XMConvertToRadians(dx));
    }
  }
}

void DxView::OnMouseWheel(int delta) {
  if (!is_take_control_) {
    return;
  }

  if (camera_type_ == CameraType::SPHERICAL) {
    spherical_camera_.OnWheelZoom(delta);
  } else if (camera_type_ == CameraType::NORMAL) {
  }
}

void DxView::PerformFrustumCulling() {
  if (!frustum_culling_enabled_) {
    return;
  }

  // 得到观察矩阵的逆矩阵。这里的思路不是将物体变换到标准的视椎体空间。而是去变换视椎体！
  XMMATRIX view = XMLoadFloat4x4(&view_);
  auto det = XMMatrixDeterminant(view);
  XMMATRIX inv_view = XMMatrixInverse(&det, view);

  int total_count = 0;
  int visiable_count = 0;

  // auto buff = curr_frame_res_->
  for (auto& e : dx_->Items()) {
    // 跳过不需要视椎体裁剪的项目
    if (e->render_layer == RenderLayer::SkyBox ||
        e->render_layer == RenderLayer::Debug) {
      continue;
    }

    // 得到世界矩阵的逆矩阵
    auto world = XMLoadFloat4x4(&e->world);
    det = XMMatrixDeterminant(world);
    XMMATRIX inv_world = XMMatrixInverse(&det, world);

    // 得到由观察空间到物体局部空间的变换矩阵
    XMMATRIX view_to_local = XMMatrixMultiply(inv_view, inv_world);

    // 将摄像机视锥体由观察空间变换到物体的局部空间
    BoundingFrustum local_space_frustum;
    camera_frustum_.Transform(local_space_frustum, view_to_local);

    // 在局部空间中执行包围盒相交测试
    if (local_space_frustum.Contains(e->bounding_box) != DirectX::DISJOINT) {
      e->visible = true;
      visiable_count++;
    } else {
      e->visible = false;
    }

    total_count++;
  }
}

} // namespace ifire::dx
